package state


import "math"
import "api"
import "number"


// 使用接收两个参数且返回一个值的函数来统一表示Lua运算符
// 一元运算符简单忽略第二个参数就可以了
var (

	iadd = func(a, b int64)		int64		{ return a + b }

	fadd = func(a, b float64)	float64		{ return a + b }

	isub = func(a, b int64)		int64		{ return a - b }

	fsub = func(a, b float64)	float64		{ return a - b }

	imul = func(a, b int64)		int64		{ return a * b }

	fmul = func(a, b float64)	float64		{ return a * b }

	imod = number.IMod

	fmod = number.FMod

	pow = math.Pow

	div = func(a, b float64) 	float64		{ return a / b }

	iidiv = number.IFloorDiv	// 整数向下整除

	fidiv = number.FFloorDiv	//  浮点数向下整除

	band = func(a, b int64)		int64		{ return a & b }

	bor = func(a, b int64)		int64		{ return a | b }

	bxor = func(a, b int64)		int64		{ return a ^ b }

	shl = number.ShiftLeft

	shr = number.ShiftRight

	iunm = func(a, _ int64)		int64		{ return -a }

	funm = func(a, _ float64)	float64		{ return -a }

	bnot = func(a, _ int64)		int64		{ return ^a }

)


// 容纳整数和浮点数类型运算的结构体
type operator struct {

	metamethod		string			// 元方法名

	integerFunc		func(int64, int64)		int64

	floatFunc		func(float64, float64)	float64

}


var operators = []operator {

	operator{"__add",	iadd,		fadd},

	operator{"__sub",	isub,		fsub},

	operator{"__mul",	imul,		fmul},

	operator{"__mod",	imod,		fmod},

	operator{"__pow",	nil,		pow},

	operator{"__div",	nil,		div},

	operator{"__idiv",	iidiv,		fidiv},

	operator{"__and",	band,		nil},

	operator{"__or",	bor,		nil},

	operator{"__xor",	bxor,		nil},

	operator{"__shl",	shl,		nil},

	operator{"__shr",	shr,		nil},

	operator{"__unm",	iunm,		funm},

	operator{"__not",	bnot,		nil},
}


// 算术/按位运算
// 先根据情况从Lua栈里弹出一到两个操作数
// 然后按索引取出相应的operators实例
// 最后调用_arith()函数执行计算
// 如果计算结果不是nil值
// 表明操作数符合(或者可以转为)运算符规定的类型
// 将计算结果推入Lua栈即可
// 否则，尝试查找相应的元方法
// 如果没有元方法，调用panic()函数终止程序执行
func (self *luaState) Arith(op api.ArithOp) {

	var a, b luaValue		// operands

	b = self.stack.pop()

	if op != api.LUA_OPUNM && op != api.LUA_OPBNOT {

		a = self.stack.pop()
	} else {

		a = b
	}

	operator := operators[op]

	if result := _arith(a, b, operator); result != nil {

		self.stack.push(result)
		return
	}

	mm := operator.metamethod

	if result, ok := callMetamethod(a, b, mm, self); ok {

		self.stack.push(result)
		return
	}

	panic("arithmetic error!")
}


// 按位运算期望操作数都是(或者可以转为)整数，运算结果也是整数
// 加、减、乘、除、整除、取反运算会在两个操作数都是整数时进行整数运算，结果也是整数
// 对于其它情况，则尝试将操作数转为浮点数再执行运算
// 运算结果也是浮点数
func _arith(a, b luaValue, op operator) luaValue {

	if op.floatFunc == nil { // bitwise
		if x, ok := convertToInteger(a); ok {
			if y, ok := convertToInteger(b); ok {
				return op.integerFunc(x, y)
			}
		}
	} else { // arith
		if op.integerFunc != nil {	// add, sub, mul, mod, idiv, unm
			if x, ok := a.(int64); ok {
				if y, ok := b.(int64); ok {
					return op.integerFunc(x, y)
				}
			}
		}

		if x, ok := convertToFloat(a); ok {
			if y, ok := convertToFloat(b); ok {
				return op.floatFunc(x, y)
			}
		}
	}

	return nil

}